package cn.touna.jss.common.factory;


import org.apache.http.HttpResponse;
import org.apache.http.config.Registry;
import org.apache.http.config.RegistryBuilder;
import org.apache.http.conn.ConnectionKeepAliveStrategy;
import org.apache.http.conn.socket.ConnectionSocketFactory;
import org.apache.http.conn.socket.PlainConnectionSocketFactory;
import org.apache.http.conn.ssl.NoopHostnameVerifier;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.web.client.RestTemplateBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Scope;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.http.client.SimpleClientHttpRequestFactory;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.web.client.RestTemplate;

import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLContext;
import javax.net.ssl.X509TrustManager;
import java.security.KeyManagementException;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;

/**
 * RestTemplateConfig使全局保持统一的RestTemplate对象
 * Created by Steve.Tao on 2017/8/18.
 */
@Configuration
public class RestTemplateConfig {

    private final static Logger logger = LogManager.getLogger(RestTemplateConfig.class);

    @Value("${jss.http.readTimeout}")
    private int readTimeout;

    @Value("${jss.http.connectionTimeout}")
    private int connectionTimeout;

    /**
     * RestTemplate(beanName=httpRestTemplate)<br/>
     * http协议时使用
     *
     * @return
     */
    @Bean("httpRestTemplate")
    public RestTemplate httpRestTemplate() {
        return build(false, null);
    }

    /**
     * RestTemplate(beanName=httpsRestTemplate)<br/>
     * https协议时使用
     *
     * @return
     */
    @Scope("prototype")
    @Bean("httpsRestTemplate")
    public RestTemplate httpsRestTemplate() {
        return build(true, null);
    }

    public CloseableHttpClient acceptsUntrustedCertsHttpClient() throws KeyStoreException, NoSuchAlgorithmException, KeyManagementException {
        HttpClientBuilder b = HttpClientBuilder.create();

        // setup a Trust Strategy that allows all certificates.
        //
        final SSLContext sslContext = SSLContext.getInstance("TLS");
        sslContext.init(null, new X509TrustManager[]{new X509TrustManager() {

            @Override
            public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
            }

            @Override
            public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
            }

            @Override
            public X509Certificate[] getAcceptedIssuers() {
                return new X509Certificate[0];
            }
        }}, new SecureRandom());
        b.setSSLContext(sslContext);

        // don't check Hostnames, either.
        //      -- use SSLConnectionSocketFactory.getDefaultHostnameVerifier(), if you don't want to weaken
        HostnameVerifier hostnameVerifier = NoopHostnameVerifier.INSTANCE;

        // here's the special part:
        //      -- need to create an SSL Socket Factory, to use our weakened "trust strategy";
        //      -- and create a Registry, to register it.
        //
        SSLConnectionSocketFactory sslSocketFactory = new SSLConnectionSocketFactory(sslContext, hostnameVerifier);
        Registry<ConnectionSocketFactory> socketFactoryRegistry = RegistryBuilder.<ConnectionSocketFactory>create()
                .register("http", PlainConnectionSocketFactory.getSocketFactory())
                .register("https", sslSocketFactory)
                .build();

        // now, we create connection-manager using our Registry.
        //      -- allows multi-threaded use
        PoolingHttpClientConnectionManager connMgr = new PoolingHttpClientConnectionManager(socketFactoryRegistry);
        connMgr.setMaxTotal(200);
        connMgr.setDefaultMaxPerRoute(100);
        b.setConnectionManager(connMgr);

        final ConnectionKeepAliveStrategy myStrategy = new ConnectionKeepAliveStrategy() {
            @Override
            public long getKeepAliveDuration(HttpResponse httpResponse, org.apache.http.protocol.HttpContext httpContext) {
                return readTimeout;//设置一个链接的最大存活时间
            }
        };
        b.setKeepAliveStrategy(myStrategy);
        CloseableHttpClient client = b.build();
        return client;
    }

    public RestTemplate build(boolean https, HttpMessageConverter converter) {
        return build(https,converter,connectionTimeout,readTimeout);
    }

    public RestTemplate build(boolean https, HttpMessageConverter converter,int connectionTimeout,int readTimeout) {
        RestTemplateBuilder builder = new RestTemplateBuilder().setConnectTimeout(connectionTimeout).setReadTimeout(readTimeout);
        if (https) {
            try {
                builder.requestFactory(new HttpComponentsClientHttpRequestFactory(acceptsUntrustedCertsHttpClient()));
            } catch (Exception e) {
                logger.error("初始化htts的RestTemplate失败，", e);
            }
        }
        if (converter != null) {
            builder.additionalMessageConverters(converter);
        }
        SimpleClientHttpRequestFactory requestFactory = new SimpleClientHttpRequestFactory();
        requestFactory.setReadTimeout(readTimeout);
        requestFactory.setConnectTimeout(connectionTimeout);
        RestTemplate restTemplate = builder.build();
        restTemplate.setRequestFactory(requestFactory);
        return restTemplate;
    }

    public int getReadTimeout() {
        return readTimeout;
    }

    public int getConnectionTimeout() {
        return connectionTimeout;
    }
}
